package com.bzkj.quality.service.impl;


import com.alibaba.fastjson2.JSONObject;
import com.bzkj.common.core.domain.AjaxResult;
import com.bzkj.common.utils.MessageUtil;
import com.bzkj.quality.domain.QmsCheckDTO;
import com.bzkj.quality.domain.QmsCheckInfo;
import com.bzkj.quality.domain.QmsMaterialInfo;
import com.bzkj.quality.enums.FirstVerdict;
import com.bzkj.quality.mapper.QmsMapper;
import com.bzkj.quality.service.QmsService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @projectName: bzkj-wms
 * @package: com.bzkj.inventory.service.impl
 * @className: QMServiceImpl
 * @description: QMS系统对接服务层接口实现类
 * @author: C1309
 * @createDate: 2024年11月07日
 * @version: v1.0
 */
@Service
public class QmsServiceImpl implements QmsService {

    @Resource
    private QmsMapper qmsMapper;

    @Resource
    private MessageUtil messageUtil;

    @Value("${qms.baseUrl}")
    private String qmsUrl;
    @Value("${qms.sourceSystem}")
    private String qmsSourceSystem;
    @Value("${qms.targetSystem}")
    private String qmsTargetSystem;
    @Value("${qms.serviceName}")
    private String qmsServiceName;
    @Value("${qms.PathString}")
    private String qmsPathString;

    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 获取物料信息
     *
     * @param billCode    单据号
     * @param cgeneralbid 表体主键
     * @param modeType    0:创建请验单 1:复验
     * @return 查询结果
     */
    @Override
    public QmsMaterialInfo getMaterialInfo(String billCode, String cgeneralbid, int modeType) {
        String suffix = checkTrOrCr(billCode);
        QmsMaterialInfo materialInfo = qmsMapper.getMaterialInfo(suffix, cgeneralbid, billCode);
        // 供应商代码
        String vbcdef2 = materialInfo.getVbcdef2();
        // 物料编码
        String materialCode = materialInfo.getMaterialCode();
        // 物料GMP码
        String itemCode = materialInfo.getvItemCode();
        if (vbcdef2 != null && !vbcdef2.isEmpty()) {
            materialInfo.setvItemCode(vbcdef2);
        } else if (itemCode != null && !itemCode.isEmpty()) {
            materialInfo.setvItemCode(itemCode);
        } else if (materialCode != null && !materialCode.isEmpty()) {
            materialInfo.setMaterialCode(materialCode);
        } else {
            materialInfo.setvItemCode("");
        }

        if (isLessThanToZero(materialInfo.getNshouldnum())) {
            throw new RuntimeException("单据负单不支持发起请验，请核对后重试！");
        }
        if (modeType == 0) {
            AjaxResult ajaxResult = messageUtil.qmsBillDetail(materialInfo.getSkuState());
            if (!ajaxResult.isSuccess()) {
                throw new RuntimeException(ajaxResult.get("msg").toString());
            }
        }
        return materialInfo;
    }

    /**
     * 复验期获取基础数据
     *
     * @param vbillcode   单据号
     * @param pkMaterial  物料主键
     * @param pkBatchcode 物料批次主键
     * @param vbatchcode  批次
     * @return 基础信息
     */
    @Override
    public Map<String, String> reInspection(String vbillcode, String pkMaterial, String pkBatchcode, String vbatchcode) {
        HashMap<String, String> hashMaps = new HashMap<>(2);
        // 查询单据号
        vbillcode = qmsMapper.selectVbillcode(pkMaterial, vbatchcode);

        String suffix = checkTrOrCr(vbillcode);
        // 获取表体主键
        String bid = qmsMapper.getBid(vbillcode, pkMaterial, pkBatchcode, vbatchcode, suffix);
        if (bid != null) {
            hashMaps.put("cgeneralbid", bid);
        }
        hashMaps.put("vbillcode", vbillcode);
        return hashMaps;
    }

    /**
     * 期初库存复验获取基础数据
     *
     * @param id 库存ID
     * @return 基础信息
     */
    @Override
    public QmsMaterialInfo reInspectionChenChen(String id) {
        QmsMaterialInfo qmsMaterialInfo = qmsMapper.getChenChenMaterialInfo(id);
        // 物料编码
        String materialCode = qmsMaterialInfo.getMaterialCode();
        // 物料GMP码
        String itemCode = qmsMaterialInfo.getvItemCode();
        if (itemCode != null && !itemCode.isEmpty()) {
            qmsMaterialInfo.setvItemCode(itemCode);
        } else if (materialCode != null && !materialCode.isEmpty()) {
            qmsMaterialInfo.setMaterialCode(materialCode);
        } else {
            qmsMaterialInfo.setvItemCode("");
        }
        return qmsMaterialInfo;
    }

    /**
     * 创建请验单
     *
     * @param qmsCheckDTO 请验单提交数据
     * @return 创建结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult createQmsCheckInForm(QmsCheckDTO qmsCheckDTO) {
        // 用于构建错误信息
        StringBuilder errorMessage = new StringBuilder();

        // 校验必填字段
        List<String> validateErrors = validateRequiredFields(qmsCheckDTO);
        if (!validateErrors.isEmpty()) {
            for (String validateError : validateErrors) {
                errorMessage.append(validateError).append("\n");
            }
            return AjaxResult.warn(errorMessage.toString());
        }
        // 验证有效期至和复验期至是否同时填写
        validateDates(qmsCheckDTO);
        // 检验当初验结论为'其他'时，符合规定数量和不符合规定数量是否填写
        validateCheckPurposeAndFirstVerdict(qmsCheckDTO);
        // 提取表体主键
        String cgeneralbid = qmsCheckDTO.getCgeneralbid();
        // 提取单据号
        String vbillcode = qmsCheckDTO.getVbillcode();
        // 检查单据是否可以发起请验
        QmsMaterialInfo materialInfo;
        String suffix = "";
        if (!vbillcode.equals("synchronous")) {
            suffix = checkTrOrCr(vbillcode);
            materialInfo = getMaterialInfo(vbillcode, cgeneralbid, qmsCheckDTO.getModeType());
        } else {
            materialInfo = reInspectionChenChen(qmsCheckDTO.getId());
        }

        qmsCheckDTO.setPkMaterial(materialInfo.getPkMaterialId());

        // 向QMS系统发起请验
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = createHttpPost(qmsCheckDTO);
            AjaxResult ajaxResult = processResponse(httpClient.execute(httpPost));

            if (ajaxResult.isSuccess()) {
                // 保存请验记录
                int result = insertQmsInfo(qmsCheckDTO);
                if (result > 0) {
                    if (!vbillcode.equals("synchronous")) {
                        int i = updateBillAndDetail(vbillcode, cgeneralbid, suffix);
                        if (i == 0) {
                            AjaxResult.warn("向QMS系统发起请验与WMS保存请验记录成功，但更新明细请验状态失败！");
                        }
                    }
                } else {
                    return AjaxResult.warn("向QMS系统发起请验成功，但WMS系统保存请验状态失败！");
                }
            } else {
                return AjaxResult.warn("向QMS系统发起请验失败！");
            }
            return AjaxResult.success("向QMS系统发起请验成功！单据号：【" + vbillcode + "】");
        } catch (IOException e) {
            throw new RuntimeException("QMS系统请求失败", e);
        }
    }

    /**
     * 存储请验数据
     */
    public int insertQmsInfo(QmsCheckDTO qmsCheckDTO) {
        return qmsMapper.insertQmsInfo(qmsCheckDTO);
    }


    /**
     * 明细更新请验状态
     *
     * @param vbillcode   单据号
     * @param cgeneralbid 表体主键
     * @param suffix      表体表名后缀
     * @return 更新结果
     */
    public int updateBillAndDetail(String vbillcode, String cgeneralbid, String suffix) {
        return qmsMapper.updateBillAndDetail(suffix, cgeneralbid, vbillcode);
    }

    /**
     * 判断是否小于0
     *
     * @param nnum 明细数量
     * @return 结果
     */
    public static boolean isLessThanToZero(String nnum) {
        try {
            // 转换为 BigDecimal
            BigDecimal value = new BigDecimal(nnum);

            // 比较是否小于等于 0
            return value.compareTo(BigDecimal.ZERO) < 0;
        } catch (NumberFormatException e) {
            // 捕获格式异常
            System.err.println("Invalid number format: " + nnum);
            return false; // 或者根据需求处理异常
        }
    }

    /**
     * 判断是采购入库还是其他入库
     *
     * @param vbillcode 单据号
     * @return 表明
     */
    private String checkTrOrCr(String vbillcode) {
        if (vbillcode.contains("CR")) {
            return "material";
        } else if (vbillcode.contains("TR")) {
            return "other";
        }
        throw new IllegalArgumentException("该单据类型可能不需要发起请验，请核对后再试！");
    }

    /**
     * 验证必填字段
     *
     * @param qmsCheckDTO 请求参数
     */
    private List<String> validateRequiredFields(QmsCheckDTO qmsCheckDTO) {
        // 存储错误信息的列表
        List<String> errors = new ArrayList<>();
        // 判断是否携带单据号和表体主键
        if (!StringUtils.hasText(qmsCheckDTO.getVbillcode())) {
            errors.add("请求数据中未携带单据号！");
        }

        if (!StringUtils.hasText(qmsCheckDTO.getCgeneralbid()) && !"synchronous".equals(qmsCheckDTO.getVbillcode())) {
            errors.add("请求数据中未携带表体主键！");
        }

        if (!errors.isEmpty()) {
            return errors;
        }

        Map<String, String> requiredFields = new HashMap<>();
        requiredFields.put("物料名称", qmsCheckDTO.getProductName());
        requiredFields.put("物料编码", qmsCheckDTO.getProductGmp());
        requiredFields.put("物料规格", qmsCheckDTO.getProductSpec());
        requiredFields.put("物料包装规格", qmsCheckDTO.getProductPackSpec());
        requiredFields.put("厂家批号", qmsCheckDTO.getOriginalBatch());
        requiredFields.put("自制物料批号", qmsCheckDTO.getBatchNum());
        requiredFields.put("总数量", qmsCheckDTO.getPleaseCheckQuantity());
        requiredFields.put("总数量单位", qmsCheckDTO.getPleaseCheckQuantityUnitName());
        requiredFields.put("包装件数", qmsCheckDTO.getGrossPiece());
        requiredFields.put("请验目的", qmsCheckDTO.getCheckPurpose());
        requiredFields.put("初验结论", qmsCheckDTO.getFirstVerdict());
        requiredFields.put("物料进厂日期", qmsCheckDTO.getComeDate());
//        requiredFields.put("备注", qmsCheckDTO.getRemark());
        requiredFields.put("生产企业", qmsCheckDTO.getManufacturerName());
        requiredFields.put("生产日期", qmsCheckDTO.getProductionDate());
        requiredFields.put("创建人名", qmsCheckDTO.getCreateUserName());
        requiredFields.put("创建人工号", qmsCheckDTO.getCreateUserJobNum());
        requiredFields.put("是否全检", qmsCheckDTO.getAllTest());

        // 遍历并检查是否有空字段
        for (Map.Entry<String, String> entry : requiredFields.entrySet()) {
            if (!StringUtils.hasText(entry.getValue())) {
                errors.add(entry.getKey() + "为必填项");
            }
        }

        // 返回错误列表
        return errors;
    }


    /**
     * 验证有效期至和复验期至是否同时填写
     *
     * @param qmsCheckDTO 请求参数
     */
    private void validateDates(QmsCheckDTO qmsCheckDTO) {
        if (!StringUtils.hasText(qmsCheckDTO.getValidityDate()) && !StringUtils.hasText(qmsCheckDTO.getReinspectDate())){
            throw new IllegalArgumentException("有效期至和复验期至不能同时为空");
        }
        if (StringUtils.hasText(qmsCheckDTO.getValidityDate()) && StringUtils.hasText(qmsCheckDTO.getReinspectDate())) {
            throw new IllegalArgumentException("有效期至和复验期至不能同时填写");
        }
        if (!StringUtils.hasText(qmsCheckDTO.getValidityStr()) && !StringUtils.hasText(qmsCheckDTO.getReinspectStr())) {
            throw new IllegalArgumentException("有效期和复验期不能同时为空");
        }
        if (StringUtils.hasText(qmsCheckDTO.getValidityStr()) && StringUtils.hasText(qmsCheckDTO.getReinspectStr())) {
            throw new IllegalArgumentException("有效期和复验期不能同时填写");
        }
    }

    /**
     * 检验当初验结论为'其他'时，符合规定数量和不符合规定数量是否填写
     *
     * @param qmsCheckDTO 请求参数
     */
    private void validateCheckPurposeAndFirstVerdict(QmsCheckDTO qmsCheckDTO) {
        String firstVerdict = qmsCheckDTO.getFirstVerdict();

        // 验证初验结论有效性
        if (firstVerdict == null ||
                (!firstVerdict.equals(FirstVerdict.OTHER.getDescription()) &&
                        !firstVerdict.equals(FirstVerdict.CONFORM.getDescription()) &&
                        !firstVerdict.equals(FirstVerdict.NON_CONFORM.getDescription()))) {
            throw new IllegalArgumentException("初验结论选择或填写错误，请检查后重试！");
        }

        // 特殊验证：当初验结论为"其他"时
        if (FirstVerdict.OTHER.getDescription().equals(firstVerdict)) {
            if (!StringUtils.hasText(qmsCheckDTO.getProductConformQuantity()) ||
                    !StringUtils.hasText(qmsCheckDTO.getProductNotConformQuantity())) {
                throw new IllegalArgumentException("当初验结论为'其他'时，符合规定数量和不符合规定数量为必填项");
            }
        }
    }

    /**
     * 发送HttpPost请求
     *
     * @param qmsCheckInfo 请求参数
     * @return HttpPost 请求
     * @throws JsonProcessingException Json转换异常
     */
    private HttpPost createHttpPost(QmsCheckInfo qmsCheckInfo) throws JsonProcessingException {
        HttpPost httpPost = new HttpPost(qmsUrl);
        httpPost.setHeader("SourceSystem", qmsSourceSystem);
        httpPost.setHeader("TargetSystem", qmsTargetSystem);
        httpPost.setHeader("ServiceName", qmsServiceName);
        httpPost.setHeader("PathString", qmsPathString);
        StringEntity entity = new StringEntity(convertRequestToJson(qmsCheckInfo), StandardCharsets.UTF_8);
        httpPost.setEntity(entity);
        return httpPost;
    }

    /**
     * 处理HttpResponse
     *
     * @param response http响应
     * @return 结果
     * @throws IOException IO异常
     */
    private AjaxResult processResponse(CloseableHttpResponse response) throws IOException {
        Map resultMap = objectMapper.readValue(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8), Map.class);
        if (resultMap.get("code").equals(0) && "success".equals(resultMap.get("msg"))) {
            return AjaxResult.success("QMS请验单提交成功!");
        }
        return AjaxResult.warn("QMS请验单提交失败!");
    }

    /**
     * 将请求参数转换为JSON格式
     *
     * @param request 请求参数
     * @return json字符串
     */
    public String convertRequestToJson(QmsCheckInfo request) {
        try {

            String jsonString = JSONObject.toJSONString(request);
            return "[" + jsonString + "]";
        } catch (Exception e) {
            throw new RuntimeException("转换为JSON格式失败", e);
        }
    }
}
